package com.goldfish666.remote_control.service.base;

import com.goldfish666.remote_control.exception.BaseException;
import com.goldfish666.remote_control.exception.BizException;
import com.goldfish666.remote_control.exception.DataNotFoundException;
import com.querydsl.core.types.Predicate;
import com.tuyang.beanutils.BeanCopyUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

import javax.persistence.EntityNotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

public interface ComplexService<TDto, TEntity, TKey> extends SimpleService<TEntity, TKey> {

    Map<Long, Class> DTO_CLASS_MAP = new HashMap<>();
    Map<Long, Class> ENTITY_CLASS_MAP = new HashMap<>();

    /**
     * 新增Dto
     *
     * @param dto
     * @return
     */
    TDto insertDto(TDto dto);

    /**
     * 更新Dto
     *
     * @param dto
     * @return
     */
    TDto updateDto(TDto dto);

    /**
     * 保存Dto
     *
     * @param dto
     * @return
     */
    TDto saveDto(TDto dto);

    /**
     * 根据主键查询实体
     *
     * @param id
     * @return
     */
    TDto getDtoById(TKey id);

    /**
     * 获取全部实体
     *
     * @return
     */
    List<TDto> getAllDtos();

    /**
     * 获取所有数据
     *
     * @return 操作结果
     */
    default List<TDto> getList() {
        return this.getList((Specification) null);
    }

    /**
     * 获取所有数据
     *
     * @param specification 查询条件
     * @return 操作结果
     */
    default List<TDto> getList(Specification<TEntity> specification) {
        return getList(specification, (Sort) null);
    }

    /**
     * 获取所有数据
     *
     * @param specification 查询条件
     * @param sort          排序条件
     * @return 操作结果
     */
    List<TDto> getList(Specification<TEntity> specification, Sort sort);

    /**
     * 自定义object转FilterSearch list
     *
     * @param specification 构造查询条件
     * @param pageable      分页信息
     * @return 操作结果
     */
    Page<TDto> getPagedDtos(Specification<TEntity> specification, Pageable pageable);

    /**
     * 采用querydsl查询
     *
     * @param predicate
     * @param pageable
     * @return
     */
    Page<TDto> getListByPredicate(Predicate predicate, Pageable pageable);

    /**
     * 实体转dto
     *
     * @param entity     实体
     * @param copyConfig 复制配置
     * @return 业务数据对象
     */
    default TDto entityToDto(TEntity entity, Class copyConfig) {
        if (entity == null) {
            throw new BizException("实体为空");
        }
        return copy(entity, getDtoClass(), copyConfig);
    }

    /**
     * 实体转dto重载
     *
     * @param entity 实体
     * @return 业务数据对象
     */
    default TDto entityToDto(TEntity entity) {
        return entityToDto(entity, null);
    }

    /**
     * dto转实体
     *
     * @param dto        业务数据对象
     * @param copyConfig 复制配置
     * @return 实体
     */
    default TEntity dtoToEntityWithConfig(TDto dto, Class copyConfig) {
        if (dto == null) {
            throw new BaseException();
        }
        return copy(dto, getEntityClass(), copyConfig);
    }

    /**
     * dto转实体重载
     *
     * @param dto 业务数据对象
     * @return 实体
     */
    default TEntity dtoToEntity(TDto dto) {
        return dtoToEntityWithConfig(dto, null);
    }

    default TEntity dtoToEntity(TDto dto, TEntity entity) {
        return dtoToEntityWithConfig(dto, entity, null);
    }

    default TEntity dtoToEntityWithConfig(TDto dto, TEntity entity, Class copyConfig) {
        if (dto == null) {
            throw new BaseException();
        }
        return copy(dto, entity, copyConfig);
    }

    /**
     * dtoList转实体List重载
     *
     * @param dtos
     * @return
     */
    default TEntity dtoListToEntityList(Iterable<TDto> dtos) {
        return dtoListToEntityList(dtos, null);
    }

    /**
     * dtoList转实体List
     *
     * @param dtos
     * @param copyConfig
     * @return
     */
    default TEntity dtoListToEntityList(Iterable<TDto> dtos, Class copyConfig) {
        if (dtos == null) {
            throw new BaseException();
        }
        return copy(dtos, getEntityClass(), copyConfig);
    }

    default <E> E copy(Object from, Class toClass) {
        return copy(from, toClass, null);
    }

    /**
     * 复制源对象中的数据到指定类型的新实例中
     *
     * @param from       源对象
     * @param toClass    指定类型
     * @param copyConfig 复制配置
     * @param <E>        指定类型
     * @return 指定类型实例
     */
    @SuppressWarnings("unchecked")
    default <E> E copy(Object from, Class toClass, Class copyConfig) {
        E to;
        try {
            to = (E) toClass.newInstance();
            to = copy(from, to, copyConfig);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new BizException();
        } catch (EntityNotFoundException ex) {
            throw new DataNotFoundException();
        }
        return to;
    }

    default <E> E copy(Object from, E to) {
        return copy(from, to, null);
    }

    @SuppressWarnings("unchecked")
    default <E> E copy(Object from, E to, Class copyConfig) {
        try {
            if (copyConfig != null) {
                BeanCopyUtils.copyBean(from, to, copyConfig);
            } else {
                BeanCopyUtils.copyBean(from, to);
            }

        } catch (EntityNotFoundException ex) {
            throw new DataNotFoundException();
        }
        return to;
    }

    /**
     * 获取业务数据对象类型
     *
     * @return 业务数据对象类型
     */
    default Class getDtoClass() {
        long code = getClass().hashCode();
        if (DTO_CLASS_MAP.containsKey(code)) {
            return DTO_CLASS_MAP.get(code);
        }
        Type[] types = this.getClass().getInterfaces()[0].getGenericInterfaces();
        Type[] ttypes = ((ParameterizedType) types[0]).getActualTypeArguments();
        Class clazz = (Class) ttypes[0];
        DTO_CLASS_MAP.put(code, clazz);
        return clazz;
    }

    /**
     * 获取数据实体类型
     *
     * @return 数据实体类型
     */
    default Class getEntityClass() {
        long code = getClass().hashCode();
        if (ENTITY_CLASS_MAP.containsKey(code)) {
            return ENTITY_CLASS_MAP.get(code);
        }
        Type[] types = this.getClass().getInterfaces()[0].getGenericInterfaces();
        Type[] ttypes = ((ParameterizedType) types[0]).getActualTypeArguments();
        Class clazz = (Class) ttypes[1];
        ENTITY_CLASS_MAP.put(code, clazz);
        return clazz;
    }


    /**
     * 递归实体 跟下级建立双向关系
     *
     * @param entity
     * @return
     */
    default Object recurseChildrenParent(Object entity) {
        Method childrenMethod;

        try {
            childrenMethod = entity.getClass().getMethod("getChildren");

            Field childField = entity.getClass().getDeclaredField("children");
            childField.setAccessible(true);
            if (StringUtils.isEmpty(childField.get(entity))) {
                Class<?> childClass = Class.forName(childField.getType().getName());
                if (Collection.class.isAssignableFrom(childClass)) {
                    Collection collection = (Collection) childClass.newInstance();
                    childField.set(entity, collection);
                }
            }
            ((Collection<?>) childrenMethod.invoke(entity)).forEach(p -> {
                try {
                    recurseChildrenParent(p);
                    Method parentMethod = p.getClass().getMethod("setParent", p.getClass());
                    parentMethod.invoke(p, entity);
                } catch (Exception ignored) {
                }
            });
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).warning(e.getMessage());
            return entity;
        }
        return entity;
    }
}
